#if (ZQ_SIMU_ML307 || ZQ_SIMU_EC800)

// #include "cm_modem.h"
// #include "cm_modem_info.h"
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#include "zq_app.h"
#include "zq_hal.h"
#include "simu.h"

// 模拟设备参数 ===================================================================================================
#define simu_imei "000061062461005"  // "869167070091282" // "860061062461372" // "864380077012939" "864380077012970"
#define simu_imsi "460066632314352"
#define simu_iccid "89860622320009143521"
#define simu_ip_addr 0x12345678


// some debug functions
long long simu_dbg_now_us;
long long simu_dbg_frequency = -1;   // 计时器频率, 记录系统每s都少tick, -1无效
long long simu_start_time_us;
// return us level span from start=1 to start=0，or from last call to next call
long long SimuDbgGetSpanUs(bool start)
{
    // 初始化高精度计时器 - for debug
    if (simu_dbg_frequency == -1) {
        LARGE_INTEGER frequency;
        QueryPerformanceFrequency(&frequency);
        simu_dbg_frequency = frequency.QuadPart;
    }

    LARGE_INTEGER now;
    QueryPerformanceCounter(&now);
    long long last = simu_dbg_now_us;
    simu_dbg_now_us = now.QuadPart;
    if (start) return 0;
    return (simu_dbg_now_us - last) / (simu_dbg_frequency / 1000000);
}



// os functions ===================================================================================================
// sleep fuctions -------------------------------------------------------------
void ZqSleepMS(uint32_t ms)
{
	Sleep(ms);
}
void ZqShutdown(void)
{
    exit(0);
}
void ZqSysDisableSleep(void) { return; }

// mutex fuctions -------------------------------------------------------------
int ZqMutexCreate(ZqMutexHandle *mutex)
{

    return pthread_mutex_init(mutex, NULL);
}
int ZqMutexDestroy(ZqMutexHandle mutex)
{
    return pthread_mutex_destroy(&mutex);
}
int ZqMutexLock(ZqMutexHandle mutex, uint32_t timeout)
{
    struct timespec ts;
    timespec_get(&ts, TIME_UTC);  // 直接获取UTC时间, C11标准
    // 设置超时时间为当前时间 + timeout秒
    ts.tv_sec += timeout / 1000;
    ts.tv_nsec += ((timeout % 1000) * 1000000);
    if (ts.tv_nsec >= 1000000000) {
        ts.tv_nsec -= 1000000000;
        ts.tv_sec++;
    }
    if(timeout == 0)
        return pthread_mutex_timedlock(&mutex, NULL);
    else
        return pthread_mutex_timedlock(&mutex, &ts);
}
int ZqMutexUnlock(ZqMutexHandle mutex)
{
    return pthread_mutex_unlock(&mutex);
}
// semaphore fuctions -------------------------------------------------------------
int ZqSempCreate(ZqSempHandle *semp, int init_count, int max_count)
{
    return sem_init(semp, 0, init_count);
}
int ZqSempDestroy(ZqSempHandle semp)
{
    return 	sem_destroy(&semp);
}
int ZqSempWait(ZqSempHandle semp, uint32_t timeout)
{
    // 获取当前时间
    struct timespec ts;
    timespec_get(&ts, TIME_UTC);  // 直接获取UTC时间, C11标准
    // 设置超时时间为当前时间 + 10秒
    ts.tv_sec += timeout / 1000;
    ts.tv_nsec += ((timeout % 1000) * 1000000);
    if (ts.tv_nsec >= 1000000000) {
        ts.tv_nsec -= 1000000000;
        ts.tv_sec++;
    }
    // 等待信号量，最多10秒
    sem_timedwait(&semp, &ts);
}
int ZqSempRelease(ZqSempHandle semp)
{
    return sem_post(&semp);
}
// thread fuctions -------------------------------------------------------------
int ZqThreadCreate(ZqThreadHandle* pth, char* name, int priority, unsigned int stack_size, ThreadFunc func, void* arg)
//int ZqThreadCreate(ZqThreadHandle* pth, char* name, intunsigned int stack_size, ThreadFunc func, void* arg)
{
    int ret = pthread_create(pth, NULL, func, arg);
    if(ret != 0) return ZQ_RET_OS_ERR;
    else return ZQ_RET_OK;
}
int ZqGetPowerupReason(void) { return 0; }
// file system ===================================================================================================
// is file exist 0 - exist; -1 - not exist
bool ZqEasyFileIsExisted(const char *path)
{
    char path_buf[256];
    char* p = strchr(path, ':');
    p = (p == NULL) ? (char*)path : p + 2;
    sprintf(path_buf, ".\\app\\user_data\\%s", p);
    FILE* fd = fopen(path_buf, "rb");
	if(fd == 0) return false;
    fclose(fd);
    return true;
}
int ZqEasyGetFileSize(char* path)
{
    char path_buf[256];
    char* p = strchr(path, ':');
    p = (p == NULL) ? path : p + 2;
    sprintf(path_buf, ".\\app\\user_data\\%s", p);
    FILE* fd = fopen(path_buf, "rb");
    if (fd == NULL) return -1;
    fseek(fd, 0, SEEK_END); 
    int fsize = (int)ftell(fd);
    fclose(fd);
    return fsize;
}
// read all file content in buf
int ZqEasyFileRead(const char* path, void* buf, int size, int offset)
{
    char path_buf[256];
    char* p = strchr(path, ':');
    p = (p == NULL) ? path : p + 2;
    sprintf(path_buf, ".\\app\\user_data\\%s", p);
    FILE* fd = fopen(path_buf, "rb");
    if (fd == 0) {
        ZQLOG("@err@: Open file error! path = %s", path);
    	return ZQ_RET_FILE_ERR;
    }
    fseek(fd, offset, SEEK_SET);
    int fsize = fread(buf, 1, size, fd);
    ZQLOG("read file, path=%s, %d, ret=%d", path, size, fsize);
    fclose(fd);
    return fsize;
}
// write all buffer content to a file
int ZqEasyFileWrite(const char* path, void* buf, int size, int offset)
{
    char path_buf[256];
    char* p = strchr(path, ':');
    p = (p == NULL) ? path : p + 2;
    sprintf(path_buf, ".\\app\\user_data\\%s", p);
    uint32_t origin = SEEK_SET;
    char* mode = NULL;
    if (offset == -1) {
        mode = "wb";
        offset = 0;
    }
    else if (offset == 0x7fffffff) {
        mode = "ab+";
        origin = SEEK_END;
        //offset = 0;
    }
    else {
        if(ZqEasyFileIsExisted(path))
            mode = "rb+";
        else
            mode = "wb+";
    }
    FILE* fd = fopen(path_buf, mode);
    if (fd == 0) {
        uint32_t err = GetLastError();
        ZQLOG("@err@: Open file error! path = %s", path);
        return ZQ_RET_FILE_ERR;
    }
    if(offset != 0x7fffffff)
        fseek(fd, offset, origin);
    int fsize = fwrite(buf, 1, size, fd);
    ZQLOG("write file, path=%s, %d, ret=%d", path, size, fsize);
    fclose(fd);
    if (fsize != size){
        ZQLOG("@err@: Write file error! path = %s", path);
    	return ZQ_RET_FILE_ERR;
    }
    return size;
}
// mkdir
int ZqEasyMkDir(const char *path)
{
    char path_buf[256];
    char* p = strchr(path, ':');
    p = (p == NULL) ? path : p + 2;
    sprintf(path_buf, ".\\app\\user_data\\%s", p);
    int ret = _mkdir(path_buf);
    if (ret == -1) return ZQ_RET_FILE_ERR;
    return ZQ_RET_OK;
}
int ZqEasyFileRemove(const char* path)
{
    char path_buf[256];
    char* p = strchr(path, ':');
    p = (p == NULL) ? path : p + 2;
    sprintf(path_buf, ".\\app\\user_data\\%s", p);
    remove(path_buf);
    return ZQ_RET_OK;
}
int ZqEasyFileRename(const char* sour, const char* dest)
{
    char sour_buf[256];
    char* p = strchr(sour, ':');
    p = (p == NULL) ? sour : p + 2;
    sprintf(sour_buf, ".\\app\\user_data\\%s", p);
    char dest_buf[256];
    p = strchr(dest, ':');
    p = (p == NULL) ? dest : p + 2;
    sprintf(dest_buf, ".\\app\\user_data\\%s", p);
    return rename(sour_buf, dest_buf);
}

// system functions ===================================================================================================
// 读取UTC1970年以来的秒 UTC
#define SIMU_TIMESTAMP_2024  (1704038400) // 定义1970年1月1日开始, 2024/01/01/00/00/00 的时间戳(s)
static int simu_cali_time_sec = -SIMU_TIMESTAMP_2024;  // 有意模拟cat1时间从2024年1月1日开始
uint32_t ZqGetUTCStample(void)
{
    time_t t = time(NULL);
    return t + simu_cali_time_sec;
}
// 读取UTC1970年以来的秒 本地时间(加时区)
uint32_t ZqGetLocalStample(void)
{
    return ZqGetUTCStample() + 8 * 60 * 60;
}
// set system time
int ZqSetUTCStample(int sec)
{
    //struct tm qtm;
    //ZqTimestampToDate(sec, &qtm.tm_year, &qtm.tm_mon, &qtm.tm_mday, &qtm.tm_hour, &qtm.tm_min, &qtm.tm_sec);
    simu_cali_time_sec += sec - ZqGetUTCStample();
    return 0;
}
// set system timezone
int ZqSetUTCTimezone(int timezone)
{
    return 0;
}
// timer fuctions -------------------------------------------------------------
// note current only support 1 instance
#pragma comment(lib, "winmm.lib")
static void (*zq_timer_cb1)(void*) = NULL;
void _zq_timer_proc1(HWND unnamedParam1, UINT unnamedParam2, UINT_PTR unnamedParam3, DWORD unnamedParam4)
{
    zq_timer_cb1(NULL);
}
void CALLBACK timer_handler(UINT uTimerID, UINT uMsg, UINT_PTR dwUser, UINT_PTR dw1, UINT_PTR dw2)
{
    ((void (*)(void*))dwUser)(NULL);
}
int ZqTimerStart(ZqTimerHandle* timer, int period, void (*cb)(void*), void* arg)
{
    int ret = ZQ_RET_OK;
    zq_timer_cb1 = cb;
    timeBeginPeriod(1);
    uint32_t mTimerId = timeSetEvent(period, 0, timer_handler, (DWORD_PTR)cb, TIME_PERIODIC);
    return ret;
} 
uint32_t ZqGetSystemTickMs(void)
{
    static long long start_us = 0;

    // 初始化高精度计时器 - for debug
    if (simu_dbg_frequency == -1) {
        LARGE_INTEGER frequency;
        QueryPerformanceFrequency(&frequency);
        simu_dbg_frequency = frequency.QuadPart;
    }

    LARGE_INTEGER now;
    QueryPerformanceCounter(&now);
    if (start_us == 0) {
        start_us = now.QuadPart;
        return 0;
    }
    return (uint32_t)((now.QuadPart - start_us) / (simu_dbg_frequency / 1000000) / 1000);
}


void ZqSysReset(void)
{
    // TODOOO 暂时用结束替代
    ZqSleepMS(10 * 1000);
    exit(0);
}

// network functions ===================================================================================================
static ZqSempHandle zq_wifi_scan_sem = NULL;
bool ZqGetIMEI(char *imei, int size)
{
    imei[0] = '\0';
    if(size < ZQ_IMEI_SIZE) return false;
    strcpy(imei, simu_imei);
    return true;
}
bool ZqGetIMSI(char *imsi, int size)
{
    imsi[0] = '\0';
    if(size < ZQ_IMSI_SIZE) return false;
    strcpy(imsi, simu_imsi);
    return true;
}
bool ZqGetICCID(char *iccid, int size)
{
    iccid[0] = '\0';
    if(size < ZQ_ICCID_SIZE) return false;
    strcpy(iccid, simu_iccid);
    return true;
}
// simu 20-31 csq
static int simu_csq = 29;
int ZqGetCSQ(void)
{
    //static int span = 0;
    //if ((span++) % 60 == 0) {
    //    srand(time(0));
    //    simu_csq = 20 + (rand() % (31 - 20 + 1));
    //}
    return simu_csq;
}
// max = 6
int ZqScanCellInfo(ZqCellInfo cell_info[], int cell_info_num)
{
    if (cell_info_num > 6) return 0;
    if (simu_csq == 0 || simu_csq == 99) return 0;
    for (int i = 0; i < cell_info_num; i++) {
        cell_info[i].primary_cell = (i == 0) ? 1 : 0;
        _itoa(simu_base_station[i][0], cell_info[i].mcc, 10);
        _itoa(simu_base_station[i][1], cell_info[i].mnc, 10);
        cell_info[i].tac = simu_base_station[i][2];
        cell_info[i].cid = simu_base_station[i][3];
        cell_info[i].rssi = simu_base_station[i][4];
    }
    return cell_info_num;
}

#include <wlanapi.h>
#pragma comment(lib, "wlanapi.lib")
static bool zq_wifi_scan_init = false;
static BOOL wifi_scan_bWait = TRUE;
void WlanNotificationCallback(PWLAN_NOTIFICATION_DATA NotifData, PVOID Arg2)
{
    if (NotifData == NULL || NotifData->NotificationSource != WLAN_NOTIFICATION_SOURCE_ACM) return;

    if (NotifData->NotificationCode == wlan_notification_acm_scan_complete) wifi_scan_bWait = TRUE;
    else if (NotifData->NotificationCode == wlan_notification_acm_scan_fail) wifi_scan_bWait = FALSE;
    else wifi_scan_bWait = FALSE;
}
int ZqScanWifiInfoSync(ZqWifiInfo wifi_info[], uint8_t wifi_info_num, uint8_t timeout)
{
    char NicDesc[256];
    uint32_t dwCurVersion = 0;
    HANDLE hClient = NULL;
    PWLAN_INTERFACE_INFO_LIST pInterfaceList;
    GUID guid;

    if (ERROR_SUCCESS != WlanOpenHandle(2, NULL, &dwCurVersion, &hClient)) {
        ZQLOG("wifi open error !");
        return 0;
    }
    if (ERROR_SUCCESS != WlanEnumInterfaces(hClient, NULL, &pInterfaceList)) {
        ZQLOG("enum interface error !");
        return 0;
    }
    wcstombs(NicDesc, pInterfaceList->InterfaceInfo[0].strInterfaceDescription, sizeof(NicDesc));
    ZQLOG("Device_Name=%s", NicDesc);
    guid = pInterfaceList->InterfaceInfo[0].InterfaceGuid;
    //  || pInterfaceList->InterfaceInfo[0].isState == wlan_interface_state_disconnected
    if (pInterfaceList->InterfaceInfo[0].isState == wlan_interface_state_not_ready) {
        ZQLOG("wifi %s is not ready", NicDesc);
        return 0;
    }
    // register callback func
    uint32_t dwNotifSourcePre;
    if (ERROR_SUCCESS != WlanRegisterNotification(hClient, WLAN_NOTIFICATION_SOURCE_ACM, TRUE, (WLAN_NOTIFICATION_CALLBACK)WlanNotificationCallback, NULL, NULL, &dwNotifSourcePre)) {
        ZQLOG("wifi register callback error !");
        return 0;
    }
    if (ERROR_SUCCESS != WlanScan(hClient, &guid, NULL, NULL, NULL)) {
        ZQLOG("wifi scan error !");
        return 0;
    }
    ZQLOG("Waiting for scan complete ... ");
    while (wifi_scan_bWait) Sleep(500);
    wifi_scan_bWait = TRUE;
    // unregister callback func
    WlanRegisterNotification(hClient, WLAN_NOTIFICATION_SOURCE_ACM, TRUE, NULL, NULL, NULL, &dwNotifSourcePre);

    PWLAN_AVAILABLE_NETWORK_LIST pWlanList = (WLAN_AVAILABLE_NETWORK_LIST*)WlanAllocateMemory(sizeof(WLAN_AVAILABLE_NETWORK_LIST));
    // WLAN_AVAILABLE_NETWORK_INCLUDE_ALL_ADHOC_PROFILES | WLAN_AVAILABLE_NETWORK_INCLUDE_ALL_MANUAL_HIDDEN_PROFILES,
    if (ERROR_SUCCESS != WlanGetAvailableNetworkList(hClient, &guid, 0, NULL, &pWlanList)) {
        WlanFreeMemory(pWlanList);
        ZQLOG("wifi scan get list error !");
        return 0;
    }
    wifi_info_num = (wifi_info_num > pWlanList->dwNumberOfItems) ? pWlanList->dwNumberOfItems : wifi_info_num;
    for (int i = 0; i < wifi_info_num; i++) {
        // ZQLOG("find wifi ap ssid: %s, quality: %3d", pWlanList->Network[i].dot11Ssid.ucSSID, pWlanList->Network[i].wlanSignalQuality);
        PWLAN_BSS_LIST pBssList;
        // get bssid(mac) and lrssi
        WlanGetNetworkBssList(hClient, &guid, &(pWlanList->Network[i].dot11Ssid), pWlanList->Network[i].dot11BssType, pWlanList->Network[i].bSecurityEnabled, NULL, &pBssList);

        sprintf(wifi_info[i].mac, "%02x:%02x:%02x:%02x:%02x:%02x", pBssList->wlanBssEntries[0].dot11Bssid[0], pBssList->wlanBssEntries[0].dot11Bssid[1], 
            pBssList->wlanBssEntries[0].dot11Bssid[2], pBssList->wlanBssEntries[0].dot11Bssid[3], pBssList->wlanBssEntries[0].dot11Bssid[4], pBssList->wlanBssEntries[0].dot11Bssid[5]);
        //wifi_info[i].channel = pcell->channel_number;
        wifi_info[i].rssi = pBssList->wlanBssEntries[0].lRssi;
    }

    WlanCloseHandle(hClient, NULL);
    WlanFreeMemory(pWlanList);
    return wifi_info_num;
}
// 启动和停止modem
bool ZqStopModem(void)
{
    return true;
}
bool ZqStartModem(void)
{
    return true;
}
// get pdp state from modem, only used by online daemon task, app use ZqIsDataLinkActived()
bool ZqIsPdpActived(void)
{
    static int flag = 0;
    if(flag++ == 0) return false;
    return true;
}
static bool zq_pdp_state = false;  // 供app判断是否在线, 不用每次到modem查询, 由守护线程定时调用HalModemGetPDPState()刷新
// get data link state, data link = pdp state
bool ZqIsDataLinkActived(void)
{
    int csq = ZqGetCSQ();
    if (csq < 2 || csq == 99) {
        zq_pdp_state = false;
    }
    return zq_pdp_state;
}
// start a data call TODOOO 调整超时为60s
int ZqActiveDataLink(void)
{
    Sleep(300);
    int csq = ZqGetCSQ();
    if (csq < 2 || csq == 99) {
        zq_pdp_state = false;
        return ZQ_RET_ERR;
    }
    zq_pdp_state = true;

    // 顺便假设基站时间矫正完成
    time_t t = time(NULL);
    // set system time
    ZqSetUTCStample((int)t);
    return ZQ_RET_OK;
}
void ZqHttpEasyClose(void * http_handle)
{
    if (0x1234 == http_handle) return; // TODOOO 临时措施，后面要改
    ghttp_request* client = (ghttp_request*)http_handle;
    if(http_handle != NULL) {
        ghttp_clean(client);
        ghttp_request_destroy(client);
        return 0;
    }
}
// 开关飞行模式
int ZqSetOfflineMode(bool enable)
{
    ZqSleepMS(500);
    if (!enable) {
        ZqSleepMS(1500);
    }
    return ZQ_RET_OK;
}

typedef struct {
    ghttp_request* client;
    char* resp;
    int resp_len;
} SimuHttpArg;
static void http_perform_func(void* arg)
{
    SimuHttpArg *handle = (SimuHttpArg*)arg;
    ghttp_status status = ghttp_process(handle->client);
    handle->resp_len = ghttp_get_body_len(handle->client);
    char* p = ghttp_get_body(handle->client);
    if (p != NULL) {
        handle->resp = (char*)malloc(handle->resp_len + 1);
        memcpy(handle->resp, p, handle->resp_len);
        handle->resp[handle->resp_len] = '\0';
    }
    return;
}
// url len_addr(http://xxx.xxx.xxx:xxxx) < 128, return NULL if failed
bool ZqHttpEasyGet(const char* url, ZqEasyDataPkg* recv_pkg)
{
    int ret = ZQ_RET_OK;
    recv_pkg->data = NULL;
    recv_pkg->len = 0;
    int url_len = strlen(url);
    if(url_len < 10) {
        ZQLOG("@err@: url too short");
        return false;
    }
    ghttp_request* client = ghttp_request_new();
    if (NULL == client) return false;

    ghttp_set_type(client, ghttp_type_get);
    //ghttp_set_header(client, "Content-Type", p);
    ghttp_set_uri(client, url);

    ghttp_prepare(client);
    pthread_t pt;
    SimuHttpArg arg = {client, NULL, 0};
    pthread_create(&pt, NULL, http_perform_func, &arg);
    pthread_join(pt, NULL);

    recv_pkg->data = arg.resp;
    recv_pkg->len = arg.resp_len;
    return true;
}
bool ZqHttpEasyGetResume(const char* url, ZqEasyDataPkg* recv_pkg, int start, int len)
{
    int ret = ZQ_RET_OK;
    recv_pkg->data = NULL;
    recv_pkg->len = 0;
    int url_len = strlen(url);
    if (url_len < 10) {
        ZQLOG("@err@: url too short");
        return false;
    }
    ghttp_request* client = ghttp_request_new();
    if (NULL == client) return false;

    ghttp_set_type(client, ghttp_type_get);
    //ghttp_set_header(client, "Content-Type", p);

        // Resume download
    char header_str[64] = { 0 };
    memset(header_str, 0x00, sizeof(header_str));
    snprintf(header_str, sizeof(header_str) - 1, "bytes=%d-%d", start, start+len-1);
    ghttp_set_header(client, "Range", header_str);
    ghttp_set_uri(client, url);

    ghttp_prepare(client);
    pthread_t pt;
    SimuHttpArg arg = { client, NULL, 0 };
    pthread_create(&pt, NULL, http_perform_func, &arg);
    pthread_join(pt, NULL);

    recv_pkg->data = arg.resp;
    recv_pkg->len = arg.resp_len;
    return true;
}
#include <windows.h>
#include <winhttp.h>
#include <stdio.h>
#include <tchar.h>
bool ZqHttpEasyPost(const char* url, char* ssl_server, char* body, int body_len, ZqEasyDataPkg* recv_pkg)
{
    // 初始化变量
    HINTERNET hSession = NULL, hConnect = NULL, hRequest = NULL;
    BOOL bResults = FALSE;
    DWORD dwSize = 0, dwDownloaded = 0;
    LPSTR pszOutBuffer = NULL;

    // 1. 初始化WinHTTP会话
    hSession = WinHttpOpen(L"WinHTTP/1.0", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
    if (!hSession) {
        ZQLOG("WinHttpOpen fail. %d", GetLastError());
        goto cleanup;
    }
    int len = MultiByteToWideChar(CP_UTF8, 0, ssl_server, -1, NULL, 0);
    wchar_t *w_ssl_server = (wchar_t*)malloc(len * sizeof(wchar_t));
    MultiByteToWideChar(CP_UTF8, 0, ssl_server, -1, w_ssl_server, len);

    url = strstr(url, "//") + 2;
    url = strchr(url, '/');
    char url_buf[256];
    sprintf(url_buf, "%s?%s", url, body);
    len = MultiByteToWideChar(CP_UTF8, 0, url_buf, -1, NULL, 0);
    wchar_t* w_path = (wchar_t*)malloc(len * sizeof(wchar_t));
    MultiByteToWideChar(CP_UTF8, 0, url_buf, -1, w_path, len);
    // 2. 连接到目标服务器
    hConnect = WinHttpConnect(hSession, w_ssl_server, INTERNET_DEFAULT_HTTPS_PORT, 0);
    if (!hConnect) {
        ZQLOG("WinHttpConnect fail. %d", GetLastError());
        goto cleanup;
    }

    // 3. 创建POST请求
    hRequest = WinHttpOpenRequest(hConnect, L"POST", w_path, NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE); // HTTPS标志
    if (!hRequest) {
        ZQLOG("WinHttpOpenRequest fail. %d", GetLastError());
        goto cleanup;
    }

    // 4. 设置请求头
    LPCWSTR headers = L"Content-Type: application/json\r\n";
    bResults = WinHttpAddRequestHeaders(hRequest, headers, -1L, WINHTTP_ADDREQ_FLAG_ADD);

    // 5. 发送POST请求（无请求体）
    bResults = WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0);
    if (!bResults) {
        ZQLOG("WinHttpSendRequest fail. %d", GetLastError());
        goto cleanup;
    }

    // 6. 接收响应
    bResults = WinHttpReceiveResponse(hRequest, NULL);
    if (!bResults) {
        ZQLOG("WinHttpReceiveResponse fail. %d", GetLastError());
        goto cleanup;
    }

    // 7. 读取响应数据 -- TODOOO 暂时认为不分包
    //do {
        // 检查可用数据大小
        recv_pkg->len = 0;
        if (!WinHttpQueryDataAvailable(hRequest, &recv_pkg->len)) {
            ZQLOG("WinHttpQueryDataAvailable fail. %d", GetLastError());
            goto cleanup;
        }
        //if (!recv_pkg->len) break;  // 无更多数据
        if (!recv_pkg->len) goto cleanup;  // 无更多数据
        // 分配缓冲区
        recv_pkg->data = (LPSTR)malloc(recv_pkg->len + 1);
        // 读取数据
        ZeroMemory(recv_pkg->data, recv_pkg->len + 1);
        if (!WinHttpReadData(hRequest, recv_pkg->data, recv_pkg->len, &dwDownloaded)) {
            ZQLOG("WinHttpReadData fail. %d", GetLastError());
            free(recv_pkg->data);
            goto cleanup;
        }
    //} while (dwSize > 0);
    
    return true;

cleanup:
    // 8. 清理资源
    if (hRequest) WinHttpCloseHandle(hRequest);
    if (hConnect) WinHttpCloseHandle(hConnect);
    if (hSession) WinHttpCloseHandle(hSession);

    return false;
}
//bool ZqHttpEasyPost(const char* url, char *ssl_server, char* body, int body_len, ZqEasyDataPkg* recv_pkg)
//{
//    int ret = ZQ_RET_OK;
//    recv_pkg->data = NULL;
//    recv_pkg->len = 0;
//
//    int url_len = strlen(url);
//    if (url_len < 10) {
//        ZQLOG("@err@: url too short");
//        return false;
//    }
//    ghttp_request* client = ghttp_request_new();
//    if (NULL == client) return false;
//
//    //ghttp_set_header(client, "Content-Type", p);
//    char url_body[256];
//    sprintf(url_body, "%s?%s", url, body);
//    ghttp_set_uri(client, url_body);
//    ghttp_set_type(client, ghttp_type_post);
//    
//    ghttp_set_header(client, "Content-Type", "application/json");
//    ghttp_set_header(client, "Host", ssl_server);
//
//    ghttp_prepare(client);
//    pthread_t pt;
//    SimuHttpArg arg = { client, NULL, 0 };
//    pthread_create(&pt, NULL, http_perform_func, &arg);
//    pthread_join(pt, NULL);
//
//    recv_pkg->data = arg.resp;
//    recv_pkg->len = arg.resp_len;
//    return true;
//}


#include <windows.h>
#include <winhttp.h>
#pragma comment(lib, "winhttp.lib")

void* ZqEasyHttpPost(char* url_all, char** resp, int* resp_len) {
    HINTERNET hSession = NULL, hConnect = NULL, hRequest = NULL;
    BOOL bResults = FALSE;

    char url[512+4], url_head[128 + 4], url_path[256], url_params[512];
    url_head[0] = url_path[0] = url_params[0] = '\0';
    *resp = NULL;
    *resp_len = 0;
    int url_len = strlen(url_all);
    if (url_len < 10 || url_len > 512) {
        ZQLOG("@err@: url len(%d) too short or too long", url_len);
        return NULL;
    }
    strcpy(url, url_all);

    // 分离url port path parameters
    char *pslash = NULL, *pcolons = NULL, *pquestion = NULL;
    char *puri = NULL, *pport = NULL, *ppath = NULL, *pparams = NULL;
    int port;
    if (url[4] == 's') puri = url + 8;  // https://
    else puri = url + 7;                // http://    
    pcolons = strstr(puri, ":");  // find the port
    pslash = strstr(puri, "/");   // skip "https://", find the path
    pquestion = strstr(puri, "?");// find the parameters
    if (pquestion != NULL && strlen(pquestion) < 512) {
        pparams = pquestion + 1;
        *pquestion = '\0';
    }
    if (pslash != NULL && strlen(pslash) < 256) {
        ppath = pslash + 1;
        *pslash = '\0';
    }
    if (pcolons != NULL && strlen(pcolons) <= 5) {
        pport = pcolons + 1;
        *pcolons = '\0';
        port = atoi(pport);
    }
    else {
        port = (url[4] == 's') ? 443 : 80;
    }

    WCHAR wuri[512], wpath[1024], wparams[1024];
    MultiByteToWideChar(CP_ACP, 0, puri, -1, wuri, 512);
    MultiByteToWideChar(CP_ACP, 0, ppath, -1, wpath, 1024);
    MultiByteToWideChar(CP_ACP, 0, pparams, -1, wparams, 1024);
    // Use WinHttpOpen to obtain a session handle.
    hSession = WinHttpOpen(wuri, WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
    // Specify an HTTP server.
    if (hSession)
        hConnect = WinHttpConnect(hSession, wuri, port, 0);

    // Create an HTTP request handle.
    if (hConnect)
        hRequest = WinHttpOpenRequest(hConnect, L"POST", wpath, NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE); // WINHTTP_FLAG_SECURE for HTTPS

    // Add headers
    if (hRequest)
        bResults = WinHttpSendRequest(hRequest, L"Content-Type: application/x-www-form-urlencoded", -1L, pparams, strlen(pparams), strlen(pparams), 0);

    // End the request.
    if (bResults)
        bResults = WinHttpReceiveResponse(hRequest, NULL);

    // Keep checking for data until there is nothing left.
    static char recv[1024];
    int len = 0;
    if (bResults) {
        DWORD dwSize = 0, dwDownloaded = 0;
        do {
            // Check for available data.
            if (!WinHttpQueryDataAvailable(hRequest, &dwSize))
                ZQLOG("Error %u in WinHttpQueryDataAvailable.\n", GetLastError());

            if (!WinHttpReadData(hRequest, (LPVOID)(recv + len), dwSize, &dwDownloaded))
                printf("Error %u in WinHttpReadData.\n", GetLastError());
            len += dwDownloaded;
        } while (dwSize > 0);
    }
    // Report any errors.
    if (!bResults)
        printf("Error %d has occurred.\n", GetLastError());
    *resp = recv;
    *resp_len = len;
    // Close any open handles.
    if (hRequest) WinHttpCloseHandle(hRequest);
    if (hConnect) WinHttpCloseHandle(hConnect);
    if (hSession) WinHttpCloseHandle(hSession);

    return 0x1234;  // TODOOO 临时措施，后面要改
}






// MQTT fuctions ===================================================================================================
static int open_nb_socket(const char* addr, const char* port) {
    struct addrinfo hints = { 0 };

    hints.ai_family = AF_UNSPEC; /* IPv4 or IPv6 */
    hints.ai_socktype = SOCK_STREAM; /* Must be TCP */
    int sockfd = -1;
    int rv;
    struct addrinfo* p, * servinfo;

    /* get address information */
    rv = getaddrinfo(addr, port, &hints, &servinfo);
    if (rv != 0) {
        //fprintf(stderr, "Failed to open socket (getaddrinfo): %s\n", gai_strerror(rv));
        return -1;
    }

    /* open the first possible socket */
    for (p = servinfo; p != NULL; p = p->ai_next) {
        sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
        if (sockfd == -1) continue;

        /* connect to server */
        rv = connect(sockfd, p->ai_addr, p->ai_addrlen);
        if (rv == -1) {
            closesocket(sockfd);
            sockfd = -1;
            continue;
        }
        break;
    }

    /* free servinfo */
    freeaddrinfo(servinfo);

    /* make non-blocking */
#if !defined(_WIN32)
    if (sockfd != -1) fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL) | O_NONBLOCK);
#else
    if (sockfd != INVALID_SOCKET) {
        int iMode = 1;
        ioctlsocket(sockfd, FIONBIO, &iMode);
    }
#endif
#if defined(__VMS)
    /*
        OpenVMS only partially implements fcntl. It works on file descriptors
        but silently fails on socket descriptors. So we need to fall back on
        to the older ioctl system to set non-blocking IO
    */
    int on = 1;
    if (sockfd != -1) ioctl(sockfd, FIONBIO, &on);
#endif

    /* return the new socket fd */
    return sockfd;
}

static int mqtt_connect_state = 0;

typedef struct simu_mqtt_client {
    struct mqtt_c_client client;
    uint8_t sendbuf[64 * 1024]; /* sendbuf should be large enough to hold multiple whole mqtt messages */
    uint8_t recvbuf[64 * 1024]; /* recvbuf should be large enough any whole mqtt message expected to be received */
    int sockfd;
    pthread_t tid;
    ZqSempHandle mqtt_semp;
    MqttReplyCallback reply_cb;
} SimuMqttClient;

static SimuMqttClient* mqtt_handle = NULL;

// MQTT 接收订阅消息回调
static ZqMutexHandle hal_mqtt_msg_mutex = NULL;
static ZqMsgQueueHandle hal_mqtt_msg_queue = NULL;
#define MQTT_RECV_SIZE  2048
void simu_mqtt_callback(void** unused, struct mqtt_response_publish* published)
{
    /* note that published->topic_name is NOT null-terminated (here we'll change it to a c-string) */
    int len = published->topic_name_size + published->application_message_size + 2; // topic + \0+ payload + \0
    if (len > MQTT_RECV_SIZE) return 0;
    char* buf = (char*)malloc(len);
    memcpy(buf, published->topic_name, published->topic_name_size);
    buf[published->topic_name_size] = '\0';
    memcpy(buf + published->topic_name_size + 1, published->application_message, published->application_message_size);
    buf[len-1] = '\0';
    ZQLOG("<----- mqtt received: topic=%s, payload=%s", buf, buf + published->topic_name_size + 1);
    ZqMessage msg = { .id = ZQ_MSG_MQTT, .param1 = 0, .param2 = (size_t)buf };
    ZqMutexLock(hal_mqtt_msg_mutex, 1000);
    ZqPostMessage(hal_mqtt_msg_queue, msg);
    ZqMutexUnlock(hal_mqtt_msg_mutex);
    return 0;

}
// TODOOOO - 去掉cb 
static int ZqMqttInit(void)
{
    hal_mqtt_msg_queue = ZqMsgQueueCreate(10);
    ZQ_ASSERT(hal_mqtt_msg_queue);
    ZQ_ASSERT(ZqMutexCreate(&hal_mqtt_msg_mutex) == ZQ_RET_OK);

    // TODOOO currently only support one mqtt client instance
    static struct simu_mqtt_client mqtt_client;
    mqtt_handle = &mqtt_client;
    SimuMqttClient* ph = mqtt_handle;
    //ph->reply_cb = cb;
    ZqSempCreate(&(ph->mqtt_semp), 0, 1);
    return ZQ_RET_OK;
}
static void* simu_mqtt_client_refresher(void* client)
{
    while (1)
    {
        mqtt_sync(client);
        Sleep(100);
    }
    return NULL;
}
ZqMqttConnect(const char* phost, const char* pport, const char* client_id, const char* user_name, const char* password)
{
    int ret = ZQ_RET_OK;

    SimuMqttClient* ph = mqtt_handle;
    // make a encrypt sign    
    if (!ZqIsDataLinkActived()) return ZQ_RET_NET_ERR;

    //char* phost = host;
    //if (strlen(host) > 7 && host[4] == ':' && host[5] == '/') // remove "mqtt://" in pc platform
    //    phost = host + 7;
    //char* p = strrchr(phost, ':');
    //if (p == NULL) return ZQ_RET_PARAM_ERR;
    //char addr[256];
    //char port[16];
    //strncpy(addr, phost, p - phost);
    //addr[p - phost] = 0;
    //strcpy(port, p + 1);
    /* open the non-blocking TCP socket (connecting to the broker) */
    ph->sockfd = open_nb_socket(phost, pport);
    if (ph->sockfd == -1) return ZQ_RET_NET_ERR;
    mqtt_init(&(ph->client), ph->sockfd, ph->sendbuf, sizeof(ph->sendbuf), ph->recvbuf, sizeof(ph->recvbuf), simu_mqtt_callback);

    // Ensure we have a clean session 
    uint8_t connect_flags = MQTT_CONNECT_CLEAN_SESSION;
    ret = mqtt_connect(&ph->client, client_id, NULL, NULL, 0, user_name, password, connect_flags, ZQ_MQTT_KEEP_ALIVE);
    /* check that we don't have any errors */
    if (ret != MQTT_OK) {
        mqtt_connect_state = 0;
        return ZQ_RET_NET_ERR;
    }
    // 建立接收轮询线程
    pthread_create(&ph->tid, NULL, simu_mqtt_client_refresher, &ph->client);
    mqtt_connect_state = 1;
    return ZQ_RET_OK;
}
void ZqMqttDisconnect(void)
{
    SimuMqttClient* ph = mqtt_handle;
    if (ph->reply_cb != NULL) {
        ZQLOG("ZqMqttDisconnect ... ");
        mqtt_disconnect(&ph->client);
        ph->reply_cb = NULL;
    }
}
int ZqMqttIsConnected(void)
{
    if (!ZqIsDataLinkActived() && mqtt_connect_state != 0) { // 模拟断网
        //ZqMqttDisconnect();
        mqtt_connect_state = 0;
    }
    return mqtt_connect_state;
}
int ZqMqttSubMsg(const char *ptopic, char qos)
{
    int ret;
    SimuMqttClient* ph = mqtt_handle;
    if ((ptopic == NULL) || (qos > 2) || (qos < 0)) return ZQ_RET_ERR;
    ret = mqtt_subscribe(&ph->client, ptopic, qos);
    if(ret != MQTT_OK) return ZQ_RET_ERR;
    return ZQ_RET_OK;
}
void ZqMqttGetSubTopics(void)
{
    ZQLOG("not support, TBC");
}
int ZqMqttPublishMsg(const char *ptopic, const char *ppayload, int qos)
{
	int ret;
    SimuMqttClient* ph = mqtt_handle;
    if ((ptopic == NULL) || (ppayload == NULL) || (qos > 2) || (qos < 0)) return ZQ_RET_ERR;
    ZQLOG("-----> mqtt published: ptopic: %s -- Payload: %s", ptopic, ppayload);
    //int len = strlen(ppayload);
    //for (int i = 416, to_print = 0; i < len; i += to_print) { // 底层buf限制, 超长(512-48=464)分段打印输出
    //    ZqSleepMS(50); // 等待底层缓冲区处理完成
    //    to_print = ((len - i) > 464) ? 464 : len - i;
    //    ZQLOG("%.464s", ppayload + i);
    //}

    ret = mqtt_publish(&ph->client, ptopic, ppayload, strlen(ppayload), qos);
    if (ret != MQTT_OK) return ZQ_RET_ERR;
    return ZQ_RET_OK;
}
// if received msg, return msg_str = topic + \0 + payload + \0, else return NULL
char* ZqMqttGetMsg(void)
{
    ZqMessage msg;
    ZqMutexLock(hal_mqtt_msg_mutex, 1000);
    bool ret = ZqGetMessage(hal_mqtt_msg_queue, &msg);
    ZqMutexUnlock(hal_mqtt_msg_mutex);
    if (ret)
        return (char*)msg.param2;
    else
        return NULL;
}




// uart functions ===================================================================================================
// - 注意, 阻塞式发送, 接收只是copy缓冲区, 需要app主动及时查询处理
// 这个函数被系统回调, 不做耗时处理, 只简单copy和拼包.
uint8_t uart0_recv_buf[ZQ_RX_MAX_SIZE];
short uart0_recv_len = 0;
uint8_t uart1_recv_buf[4];   // ml307r 项目不使用uart1
short uart1_recv_len = 0;

static void uart_thread_func(void* arg)
{
    while (1) {
        for (int i = 0; i < 2; i++) {
            char buf[ZQ_RX_MAX_SIZE];
            HANDLE h = (uart_handles[i]) ? uart_handles[i] : pipe_read[i]; // 使用串口还是管道模拟
            if (h == 0) continue;
            int len;
            ReadFile(h, buf, sizeof(buf), &len, NULL);
            if (i == 0) {
                if (len > (sizeof(uart0_recv_buf) - uart0_recv_len)) len = sizeof(uart0_recv_buf) - uart0_recv_len;
                memcpy((uart0_recv_buf + uart0_recv_len), buf, len);
                uart0_recv_len += len;
            }
            else if(i == 1) {
                if (len > (sizeof(uart1_recv_buf) - uart1_recv_len)) len = sizeof(uart1_recv_buf) - uart1_recv_len;
                memcpy((uart1_recv_buf + uart1_recv_len), buf, len);
                uart1_recv_len += len;
            }
        }
        ZqSleepMS(15);
    }
    return;
}
int ZqUartInit(int port, int baud)
{
    DCB dcb;
    COMMTIMEOUTS CommTimeouts;
    static pthread_t* puart_thread = NULL;
    static pthread_t uart_thread;
    if (port < 0 || port >= 16) return 1;

    // 启动接受线程
    if (puart_thread == NULL) {
        puart_thread = _beginthread(uart_thread_func, 0, 0);
    }
    // 使用匿名管道模拟
    if (pipe_read[port] != 0) {
        return;
    }

    //打开串口
    int wcom_port_len = strlen(uart_ports[port]);
    assert(wcom_port_len < 31);
    char port_str[32] = { 0 };
    if (wcom_port_len <= 4) strcpy(port_str, uart_ports[port]);     // if < COM9
    else sprintf(port_str, "\\\\.\\%s", uart_ports[port]);          // windows 设备要求> com9, 使用\\.\comxx这种格式
    wchar_t wcom_port[32] = { 0 };
    wcom_port_len = strlen(port_str) + 1;
    mbstowcs(wcom_port, port_str, wcom_port_len);
    uart_handles[port] = CreateFile(wcom_port,    //串口名称
        GENERIC_READ | GENERIC_WRITE,			//可读、可写   				 
        0,            // No Sharing                               
        NULL,         // No Security                              
        OPEN_EXISTING,// Open existing port only                     
        FILE_ATTRIBUTE_NORMAL,            // Non Overlapped I/O                           
        NULL);        // Null for Comm Devices
    if (INVALID_HANDLE_VALUE == uart_handles[port]) {
        DWORD err = GetLastError();
        return 1;
    }
    // 设置读写缓存大小
    if (!SetupComm(uart_handles[port], UART_BUF_SIZE, UART_BUF_SIZE)) {
        return 1;
    }
    //设定读写超时
    CommTimeouts.ReadIntervalTimeout = MAXDWORD;//读间隔超时 - 立刻返回
    CommTimeouts.ReadTotalTimeoutMultiplier = 0;//读时间系数
    CommTimeouts.ReadTotalTimeoutConstant = 0;//读时间常量
    CommTimeouts.WriteTotalTimeoutMultiplier = 0;//写时间系数
    CommTimeouts.WriteTotalTimeoutConstant = 200;//写时间常量 - 写总超时
    if (!SetCommTimeouts(uart_handles[port], &CommTimeouts)) { //设置超时
        return 1;
    }
    //设置串口状态属性
    GetCommState(uart_handles[port], &dcb); //获取当前
    dcb.BaudRate = baud;                    //波特率
    dcb.ByteSize = 8;                       //每个字节有位数
    dcb.Parity = 0;                         //无奇偶校验位
    dcb.StopBits = 0;                       //一个停止位
    // 关键：禁用所有流控制
    dcb.fOutxCtsFlow = FALSE;
    dcb.fOutxDsrFlow = FALSE;
    dcb.fDtrControl = DTR_CONTROL_DISABLE;
    dcb.fRtsControl = RTS_CONTROL_DISABLE;
    dcb.fInX = FALSE;
    dcb.fOutX = FALSE;
    if (!SetCommState(uart_handles[port], &dcb)) {
        return 1;
    }
    // 设置缓冲区
    SetupComm(uart_handles[port], 4096, 4096);
    // 清空缓冲区
    PurgeComm(uart_handles[port], PURGE_TXCLEAR | PURGE_RXCLEAR);
    return 0;
}
int ZqUartWrite(int port, uint8_t *data, short len)
{
    unsigned int ret = 0;
    if (uart_handles[port] != 0) {
        clock_t start = clock();
        WriteFile(uart_handles[port], data, len, &ret, NULL);
        int x = (int)(clock() - start);
        if (x > 100)
            ZqSysLog("dtu_cmd01 send cmd01 time: len=%d, t=%d \n", len, x);
    }
    else
        WriteFile(pipe_write[port], data, len, &ret, NULL);
    DWORD x = GetLastError();
    return ret;
}

// firmware funtions ===================================================================================================
int ZqFirmwareClear()
{
    char ota_file[128];
    sprintf(ota_file, "%s_ota", FIRMWARE_CAT1_FILE);
    ZqEasyFileRemove(ota_file);
    return ZQ_RET_OK; // 等待mcu先跟新重启后,重启更新Cat1 
}

// update if has the image
void ZqFirmwareUpdate(const char* formware_file)
{
    int ret = ZQ_RET_OK;

    ZQLOG("OTA 1: check if has the firmware image.");
    if (!ZqEasyFileIsExisted(formware_file))
        return;

    ZQLOG("OTA 2: start OTA with offline mode.");
    ZqSetOfflineMode(true);

    ZQLOG("OTA 3: clear old firmware.");
    ZqFirmwareClear(formware_file);

    ZQLOG("OTA 4: set ota firmware.");
    char ota_file[128];
    sprintf(ota_file, "%s_ota", formware_file);
    int len = ZqEasyGetFileSize(formware_file);
    uint8_t buf[20 * 1024];
    for (int offset = 0, sz = 0; offset < len; offset += sz) {
        sz = ZqEasyFileRead(formware_file, buf, 20 * 1024, offset);
        if (sz <= 0) {
            ZQLOG("@err@: ZqFirmwareUpdate: read cat1 img err.");
            goto ota_err;
        }
        ret = ZqEasyFileWrite(ota_file, buf, sz, offset);
        if (sz != ret) {
            ZQLOG("@err@: ZqFirmwareUpdate: set cat1 firmware file err.");
            goto ota_err;
        }
    }
    ZQLOG("OTA 5: check ota firmware image.");

    ZQLOG("OTA 6: remove the cat1 image.");
    ZqEasyFileRemove(formware_file);

    ZQLOG("OTA 7: reset to update.");

    ZQLOG("System wait for restart to OTA...");
    ZqSleepMS(1000);
    ZqSysReset();
    return ZQ_RET_OK;
ota_err:
    ZQLOG("OTA Err: clear cat1 image and reset system");
    ZqFirmwareClear();
}
// 1. check image, 2. save firmware to flash
int ZqFirmwareSave(uint8_t *firmware, int len, int crc)
{
    int ret = 0;
    if (len == 0) {
        ZQLOG("@err@: cat1 image size=%d", len);
        return ZQ_RET_ERR;
    }
    ZqEasyFileRemove(FIRMWARE_UPDATE_FILE);
    if (ZqEasyFileWrite(FIRMWARE_UPDATE_FILE, firmware, len, -1) == len)
        return ZQ_RET_OK;

    ZqEasyFileRemove(FIRMWARE_UPDATE_FILE);
    return ZQ_RET_ERR;
}
// debug functions ===================================================================================================
void ZqDbgPrintf(const char *fmt, ...)
{
    //不定参数格式化
    va_list argptr;
    char buf[1024 + 4];
    va_start(argptr, fmt);
    int cnt = vsnprintf(buf, 1024, fmt, argptr);
    va_end(argptr);
    buf[cnt] = 0;
    char *p = buf;
    char *buf_end = buf + cnt;

    do {
        int len = buf_end - p;
        if (len > 104) {
            char *log_end = p + 108;
            char tmp = *log_end;
            *log_end = 0;
            printf(p);    
            *log_end = tmp;
            p += 104;
        }
        else {
            printf(p);
            break;
        }
    }
    while(1);
    return;
}
static uint32_t zq_debug_time_span = 0;
void ZqDbgTimeSpanStart(void)
{
    // TODOOO TBC
    //zq_debug_time_span = osKernelGetTickCount();
}
uint32_t ZqDbgTimeSpanEnd(bool is_print)
{
    // TODOOO TBC
    //uint32_t span = osKernelGetTickCount() - zq_debug_time_span;
    //if (is_print) ZQLOG("dbg: time span = %dms", span);
    //return span;
    return 99999999;
}
void ZqDbgMemory(void)
{
    return;
}
// hal init ===================================================================================================
void ZqHalInit(void)
{
    // 模拟上电自动登网
    ZqActiveDataLink();
    if(zq_wifi_scan_sem == NULL) 
        ZqSempCreate(&zq_wifi_scan_sem, 0, 1);
    for(int i = 0; i < 3; i++, ZqSleepMS(100)) {
        ZQLOG("check for modem ready ... %ds", i);
    }
    // 模拟硬件平台上电自动激活一次pdp
    // ZQ_ASSERT(retry <= 25);
    // get IMEI/IMSI/ICCID info
    ZqGetIMEI(zq_dev_info.imei, sizeof(zq_dev_info.imei));
    // 读imsi会和modem通信, 可能出错, 尝试多次
    for (int i = 0; (i < 10) && (!ZqGetIMSI(zq_dev_info.imsi, sizeof(zq_dev_info.imsi))); i++, ZqSleepMS(500));
    // 读iccid会和modem通信, 可能出错, 尝试多次
    for (int i = 0; (i < 10) && (!ZqGetICCID(zq_dev_info.iccid, sizeof(zq_dev_info.iccid))); i++, ZqSleepMS(500)); // add a little delay, or it seems that we continuous operate sim functions will get an error.
    // 获取wifi mac info - imei simu
    ZQ_IMEI2MAC(zq_dev_info.wifiMAC, zq_dev_info.imei);
    ZQLOG("imei: %s, imsi: %s, iccid: %s, wifimac: %s, blemac: %s", zq_dev_info.moduleId, zq_dev_info.imsi, zq_dev_info.iccid, zq_dev_info.wifiMAC, zq_dev_info.bluetoothMAC);
    // init base device info
    zq_dev_info.manufactureCode = CFG_MANUFACTURE_CODE;
    zq_dev_info.accessType = 0;		// 接入网络方式  查询 int 0:移动网络，1:WIFI
    if (zq_dev_info.imsi[0] == 0)	// imsi get error
        ZqSetErrCode(ZQ_ERR_CODE_NO_SIM, 1);
    else
        ZqSetErrCode(ZQ_ERR_CODE_NO_SIM, 0);
    ZQ_ASSERT(zq_dev_info.imsi[0] != 0 && zq_dev_info.iccid[0] != 0);
    // init hal variables
    zq_wifi_scan_init = false;
    ZqMqttInit();
}

int ql_get_powerup_reason(void) {return 0; }

// diagnostic cmds ===================================================================================================
void ZqDiagnosticCmds(void)
{
    char input[256];
    fflush(stdin);
    while (1) {
        if (fgets(input, sizeof(input), stdin) == NULL) break;
        char *sep = strchr(input, '=');
        if(sep == NULL) continue;
        *sep = 0;

        if (strcmp(input, "4g") == 0) {
            int csq = atoi(sep + 1);
            if(csq >= 0 && csq <= 31) simu_csq = csq;
            //zq_pdp_state = (csq == 0) ? false : true;
        }
    }
}

#endif // ZQ_SIMU_ML307
